<?php

class OOWP_Element
    extends OOWP_Formattable
{

    public function __construct(array $args = array( ))
    {
        $this->dict['contents'] = new OOWP_FormatSlot($this, array(
            'null',
            'OOWP_Formattable',
            'string'
        ));

        // class defaults
        $input = $this->merge_arguments(array(
            'attributes' => array( ),
            'contents' => array( ),
            'container_decorator' => null,
            'contents_decorator' => null,
            'void' => false,
            'indent' => true,
            'tag' => 'div',
        ), $args);

        // intercepts 'contents' before initializing base class,
        // passes 'contents' to FormatSlot instead of to OOWP_Base
        if (array_key_exists('contents', $input)) {
            $contents = $input['contents'];
            unset($input['contents']);
            if ($contents) {
                foreach ($contents as $key => $value) {
                    // explicitly set as key/value pairs, even if not using keys
                    // as this allows the possibility of defining keys as helpers
                    // i.e. "body", "header", "utility"
                    // which should be able to be overridden via array_merge
                    $this->dict['contents'][$key] = $value;
                }
            }
        }

        parent::__construct($input);
    }

    /* ACCESSORS */

    protected function getAttributes( )
    {
        if (isset($this->dict['attributes'])) {
            return $this->dict['attributes'];
        }
        return null;
    }

    protected function setAttributes(array $attributes)
    {
        if ($this->is_dict_of_wellformed_strings($attributes)) {
            $this->dict['attributes'] = $attributes;
        }
    }

    protected function getContents( )
    {
        return $this->dict['contents'];
    }

    protected function getContainerDecorator( )
    {
        if (isset($this->dict['container_decorator'])) {
            return $this->dict['container_decorator'];
        }
        return null;
    }

    protected function setContainerDecorator($decorator)
    {
        if (is_null($decorator)) {
            $this->dict['container_decorator'] = null;
        } elseif ($this->is_wellformed_string($decorator)) {
            $this->dict['container_decorator'] = $decorator;
        }
    }

    protected function getContentsDecorator( )
    {
        if (isset($this->dict['contents_decorator'])) {
            return $this->dict['contents_decorator'];
        }
        return null;
    }

    protected function setContentsDecorator($decorator)
    {
        if (is_null($decorator)) {
            $this->dict['contents_decorator'] = null;
        } elseif ($this->is_wellformed_string($decorator)) {
            $this->dict['contents_decorator'] = $decorator;
        }
    }

    protected function getVoid( )
    {
        return $this->dict['void'];
    }

    protected function setVoid($empty)
    {
        $this->dict['void'] = (bool) $empty;
    }

    protected function getFormatPieces( )
    {
        if (!count($this->contents) && $this->void) {
            return array(
                '<' . $this->build_start_tag_nucleus( ) . ' />'
            );
        }
        return $this->collect_element_pieces( );
    }

    protected function getIndent( )
    {
        return $this->dict['indent'];
    }

    protected function setIndent($indent)
    {
        $this->dict['indent'] = (bool) $indent;
    }

    protected function getTag( )
    {
        return $this->dict['tag'];
    }

    protected function setTag($tag)
    {
        if ($this->is_wellformed_string($tag)) {
            $this->dict['tag'] = $tag;
        }
    }

    /* METHODS */

    protected function apply_container_decorator($pieces)
    {
        $start = '<div class="' . $this->container_decorator . '">';
        $end = '</div>';

        array_unshift($pieces, $start);
        array_push($pieces, $end);

        return $pieces;
    }

    protected function apply_contents_decorator($pieces)
    {
        $start = '<' . $this->contents_decorator . '>';
        $end = '</' . $this->contents_decorator . '>';

        array_unshift($pieces, $start);
        array_push($pieces, $end);

        return $pieces;
    }

    protected function build_start_tag_nucleus( )
    {
        $tag = $this->tag;
        $id = '';
        $class = array( );
        $attributes = $this->attributes;

        if (array_key_exists('id', $attributes)) {
            $id = $attributes['id'];
            unset($attributes['id']);
        }
        if (method_exists($this, 'filter_element_id')) {
            $id = $this->filter_element_id($id);
        }

        if (array_key_exists('class', $attributes)) {
            $class = $attributes['class'];
            unset($attributes['class']);
        }
        if (method_exists($this, 'filter_element_class')) {
            $class = $this->filter_element_class($class);
        }

        if (method_exists($this, 'filter_element_attributes')) {
            $attributes = $this->filter_element_attributes($attributes);
        }

        $parts = array($tag);

        if ($id) {
            $parts[ ] = 'id="' . $id . '"';
        }

        if ($class) {
            if (is_array($class)) {
                $parts[ ] = 'class="' . implode(' ', $class) . '"';
            } else {
                $parts[ ] = 'class="' . $class . '"';
            }
        }

        foreach ($attributes as $key => $value) {
            if (is_array($value)) {
                $attr = $key . '="' . implode(' ', $value) . '"';
            } else {
                $attr = $key . '="' . $value . '"';
            }
            $parts[ ] = $attr;
        }

        if (method_exists($this, 'filter_element_start_tag_nucleus')) {
            $parts = $this->filter_element_start_tag_nucleus($parts);
        }

        return implode(' ', $parts);
    }

    protected function collect_element_pieces( ) {
        $start = '<' . $this->build_start_tag_nucleus( ) . '>';
        $end = '</' . $this->tag . '>';

        $contents_objects = $this->contents->toArray( );
        if (method_exists($this, 'filter_element_contents_objects')) {
            $contents_objects = $this->filter_element_contents_objects($contents_objects);
        }

        if ($this->contents_decorator) {
            // i.e. to add an <li> before and after each contents Formattable
            $contents = array( );
            foreach ($contents_objects as $c) {
                $decorated = $this->apply_contents_decorator(
                    $this->collect_format_pieces($c, $this->indent));
                foreach ($decorated as $line) {
                    $contents[ ] = $line;
                }
            }
        } else {
            $contents = $this->collect_format_pieces($contents_objects, $this->indent);
        }

        if ($this->container_decorator) {
            $contents = $this->apply_container_decorator($contents);
        }

        if (method_exists($this, 'filter_element_contents')) {
            $contents = $this->filter_element_contents($contents);
        }

        array_unshift($contents, $start);
        array_push($contents, $end);
        return $contents;
    }

    protected function merge_arguments($defaults, $override)
    {
        $merged = array_merge($defaults, $override);
        // merge the 'contents' portion if both defaults and override contain string-keys
        // and if all of the keys in the overrides already exist in the defaults
        // otherwise, just use the overrides
        if (array_key_exists('contents', $defaults) &&
            array_key_exists('contents', $override)) {
            $d_numeric = (OOWP_Base::all_keys_are_numeric($defaults['contents']) ? true : false);
            $o_numeric = (OOWP_Base::all_keys_are_numeric($override['contents']) ? true : false);
//            if (($d_numeric === $o_numeric) && ($d_numeric === false)) {
//                if (OOWP_Base::all_keys_in_one_array_are_keys_in_another($override['contents'], $defaults['contents'])) {
                    $merged['contents'] = array_merge($defaults['contents'], $override['contents']);
//                }
//            }
        }
        return $merged;
    }

}
